/*
 * Character.h
 *
 *  Created on: 07.10.2011
 *      Author: ignacio
 */

#ifndef CHARACTER_H_
#define CHARACTER_H_

#include "vector.h"
using neurignacio::sqrt;
#include <vector>
using std::vector;
#include <string>
using std::string;
#include "SmartPointer.h"
#include "container.h"

#include <iostream>
using std::cout;
using std::endl;




namespace neurignacio
{
/*
 * **************************************************************************************************************************************
 * 																BASIC PARTICLES															*
 * **************************************************************************************************************************************
 */
/*
 * *******
 * Quark *
 * *******
 */
class Quark
// Quark is a basic class containing a vector3 member and an angle member
{
public:
	Vector3 rho;
	angle theta;

public:
	Quark(void) : rho(), theta() {}
	Quark(const Vector3& r, const angle& a=angle(0)) : rho(r), theta(a) {}
	Quark(const real& x, const real& y, const real& z, const angle& a=angle(0)) : rho(x,y,z), theta(a) {}
	Quark(const real& x, const real& y, const real& z, const real &a, const angleUnit &unit=DEFAULT_UNIT)
		: rho(x,y,z), theta(a,unit) {}
	Quark(const Quark& quark) : rho(quark.rho), theta(quark.theta) {}
	friend ostream& operator<<(ostream& out, const Quark& quark);

public:
	void setVector(const Vector3& r) {rho=r;}
	void setVector(const real& x, const real& y, const real& z) {rho.x=x; rho.y=y; rho.z=z;}
	void setAngle(const angle& alpha) {theta=alpha;}
	void setAngle(const real &a, const angleUnit &unit=DEFAULT_UNIT) {theta=angle(a, unit);}
	Vector3 getVector() const {return rho;}
	angle getAngle() const {return theta;}
public:
	Quark& operator=(const Quark& other){if (this!=&other) {rho = other.rho; theta = other.theta;} return *this;}
	void clear() {rho.clear(); theta=0;}
	bool operator == (const Quark& other) const {return rho == other.rho &&  theta == other.theta;}
	bool operator != (const Quark& other) const {return rho != other.rho ||  theta != other.theta;}
};
typedef BaseContainer<Quark> QuarkContainer;
typedef BasePointerContainer<Quark> QuarkPointerContainer;

/*
 * ****************
 * SteeringOutput *
 * ****************
 */
class SteeringOutput : public Quark
// SteeringOutput is a movement requested by the steering system.
{
public:
	Vector3& linear; // Alias for Quark::rho
	angle& angular;	// Alias for Quark::theta
	SteeringOutput(void): Quark(), linear(rho), angular(theta) {}
//	SteeringOutput(const Vector3& r, const angle& a=angle(0)) : Quark(r,a), linear(rho), angular(theta) {}
//	SteeringOutput(const real& x, const real& y, const real& z, const angle& a=angle(0)) : Quark(x,y,z,a), linear(rho), angular(theta) {}
	SteeringOutput(const SteeringOutput& steering) : Quark(static_cast<Quark>(steering)), linear(rho), angular(theta) {}
	Vector3 getLinear(void) const {return linear;}
	angle getAngular(void) const {return angular;}
	// Specific Members
	real squareMagnitude() const {return linear.squareMagnitude();}//{return angular*angular + linear.squareMagnitude();}
	real magnitude() const {return linear.magnitude();}
	SteeringOutput& operator=(const SteeringOutput& rhs) 	{if (this!=&rhs) {this->linear=rhs.linear;this->angular=rhs.angular;} return *this;}

	SteeringOutput operator+(const SteeringOutput& steering) const
	{
		SteeringOutput output;
		output.linear=(this->linear+steering.linear);
		output.angular=(this->angular+steering.angular);
		return output;
	}
	void operator+=(const SteeringOutput& steering) {*this=(*this)+steering;}
	SteeringOutput operator-(const SteeringOutput& steering) const {SteeringOutput output; output.linear=(this->linear-steering.linear); output.angular=(this->angular-steering.angular); return output;}
	void operator-=(const SteeringOutput& steering) {*this=(*this)-steering;}
	friend ostream& operator<<(ostream& out, const SteeringOutput& steering);
};
typedef BaseContainer<SteeringOutput> SteeringOutputContainer;
typedef BasePointerContainer<SteeringOutput> SteeringOutputPointerContainer;

#define NULL_STEERING SteeringOutput()

/*
 * **********
 * Location *
 * **********
 */
class Location : public Quark
// Location is a particular Quark in which rho and theta are called position and orientation
{
public:
	Vector3& position;
	angle& orientation;
	Location(void) : Quark(), position(rho), orientation(theta) {}
	Location(const Location& location) : Quark(static_cast<Quark>(location)), position(rho), orientation(theta) {}
	Vector3 getPosition() const {return position;}
	angle getOrientation() const {return orientation;}
	void setPosition(const Vector3& pos) {position=pos;}
	void setOrientation(const angle& orient) {orientation=orient;}
	// Specific Members
	void integrate(const SteeringOutput& steer, real duration);
	void setOrientationFromVelocity(const Vector3& velocity);
	Vector3 getOrientationAsVector() const;
	// From Kinematic
	bool operator < (const Location& other) const
	        {return position.x < other.position.x;}
	Location& operator=(const Location& other)
	{
		if (this!=&other) {static_cast<Quark>(*this)=static_cast<Quark>(other); position=other.position; orientation=other.orientation;} return *this;
	}
};
typedef BaseContainer<Location> LocationContainer;
typedef BasePointerContainer<Location> LocationPointerContainer;

#define NULL_LOCATION_POINTER static_cast<Location*>(NULL)

/*
 * ********
 * Lepton *
 * ********
 */
class Lepton: public Location
// Lepton is a Location-Quark with velocity and rotation
{
public:
	Vector3 velocity;
	angle rotation;
public:
	Lepton(void) : Location(), velocity(), rotation() {}
	Lepton(const Lepton& lepton) : Location(static_cast<Location>(lepton)), velocity(lepton.velocity), rotation(lepton.rotation) {}
	Vector3 getVelocity() const {return velocity;}
	angle getRotation() const {return rotation;}
	void setVelocity(const Vector3& v) {velocity=v;}
	void setRotation(const angle& alpha) {rotation=alpha;}
	void clear() {Location::clear(); velocity.clear(); rotation = 0.0f;}
	bool operator == (const Lepton& other) const;
	bool operator != (const Lepton& other) const;
	Lepton& operator = (const Location& other);
	Lepton& operator = (const Lepton& other);
	void operator += (const Lepton&);
	void operator -= (const Lepton&);
	void operator *= (real f);
	void integrate(real duration);
	void integrate(const SteeringOutput& steer, real duration);
	void integrate(const SteeringOutput& steer, real drag, real duration);
	void integrate(const SteeringOutput& steer, const SteeringOutput& drag, real duration);
	void trimMaxSpeed(const real& maxSpeed);
	void trimMaxRotation(const angle& maxRotation);
	void setOrientationFromVelocity() {Location::setOrientationFromVelocity(velocity);}
	friend ostream& operator<<(ostream& out, const Lepton& lepton);
};
typedef BaseContainer<Lepton> LeptonContainer;
typedef BasePointerContainer<Lepton> LeptonPointerContainer;

/*
 * ******
 * Muon *
 * ******
 */
class Muon : public Lepton
// Muon is a Lepton with some velocity/acceleration limits
{
public:
	real maxVelocity;
	real& maxSpeed; // Alias for maxVelocity
	angle maxRotation;
	real minVelocity; // Velocity to be considered as Zero
	/**
	 * The maximum acceleration that can be used to reach the
	 * target.
	 */
	real maxAcceleration;
	angle maxAngularAcceleration;
public:
	Muon(void) : Lepton(), 	maxVelocity(0), maxSpeed(maxVelocity), maxRotation(0) ,
							maxAcceleration(0), maxAngularAcceleration(0), minVelocity(0) {}
	Muon(const Muon& muon): Lepton(static_cast<Lepton>(muon)), maxVelocity(muon.maxVelocity), maxRotation(muon.maxRotation), maxSpeed(maxVelocity)
																, maxAcceleration(muon.maxAcceleration), maxAngularAcceleration(muon.maxAngularAcceleration), minVelocity(muon.minVelocity) {}
	Muon& operator=(const Muon& other)
	{
		if (this!=&other)
		{
			static_cast<Lepton>(*this)=static_cast<Lepton>(other);
			maxVelocity=other.maxVelocity;
			maxRotation=other.maxRotation;
			minVelocity=other.minVelocity;
			maxAcceleration=other.maxAcceleration;
			maxAngularAcceleration=other.maxAngularAcceleration;
		}
		return *this;
	}
	friend ostream& operator<<(ostream& out, const Muon& muon);
};
typedef BaseContainer<Muon> MuonContainer;
typedef BasePointerContainer<Muon> MuonPointerContainer;




} // end namespace neurignacio


#endif /* CHARACTER_H_ */
